/*
    作者：zx
    时间：2023.2.1
    描述：设备管理层定义，屏蔽所有硬件细节
*/

#include "dev/dev.h"
#include "cpu/irq.h"
#include "tools/klib.h"

#define DEV_TABLE_SIZE 128

extern device_desc_t dev_tty_desc;
extern device_desc_t dev_disk_desc;

// 设备描述符表
static device_desc_t * dev_desc_table[] = {
    &dev_tty_desc,
    &dev_disk_desc,
};

// 设备表
static device_t dev_table[DEV_TABLE_SIZE];

// 检查dev_id是否合法，对应的desc是否存在
static int is_devid_bad(int dev_id)
{
    if(dev_id < 0 || dev_id >= DEV_TABLE_SIZE)
    {
        return 1;
    }

    if(!dev_table[dev_id].desc)
    {
        return 1;
    }

    return 0;
}

// 返回设备表索引
int dev_open(int major, int minor, void * data)
{
    // 可能会被多个进程打开
    irq_status_t status = irq_enter_protection();
    device_t * free_dev = 0;
    
    // 查设备表
    for(int i=0;i<DEV_TABLE_SIZE; i++)
    {
        device_t * dev = dev_table + i;
        if(dev->open_count == 0)
        {
            free_dev = dev;
        }
        else if(dev->desc->major == major && dev->minor == minor)
        {
            dev->open_count++;
            irq_quit_protection(status);
            return i;
        }
    }
 
    // 查找设备描述符表
    device_desc_t * desc = 0;
    for(int i=0;i< sizeof(dev_desc_table) / sizeof(dev_desc_table[0]);i++)
    {
        device_desc_t * desc_in = dev_desc_table[i];
        if(desc_in->major == major)
        {
            desc = desc_in;
            break;
        }
    }

    // 如果设备表可以分配给新设备 且 描述符表中有该设备的实现
    if(free_dev && desc)
    {
        free_dev->minor = minor;
        free_dev->data = data;
        free_dev->desc = desc;

        int err = desc->open(free_dev);
        if(err == 0)
        {
            free_dev->open_count = 1;
            irq_quit_protection(status);
            return free_dev - dev_table;
        }
    }

    irq_quit_protection(status);
    return -1;
}

int dev_read(int dev_id, int addr, char * buff, int size)
{
    if(is_devid_bad(dev_id))
    {
        return -1;
    }

    device_t * dev = dev_table + dev_id;
    return dev->desc->read(dev, addr, buff, size);
}

int dev_write(int dev_id, int addr, char * buff, int size)
{
    if(is_devid_bad(dev_id))
    {
        return -1;
    }

    device_t * dev = dev_table + dev_id;
    return dev->desc->write(dev, addr, buff, size);
}


int dev_control(int dev_id, int cmd, int arg0, int arg1)
{
    if(is_devid_bad(dev_id))
    {
        return -1;
    }

    device_t * dev = dev_table + dev_id;
    return dev->desc->control(dev, cmd, arg0, arg1);
}


void dev_close(int dev_id)
{
    if(is_devid_bad(dev_id))
    {
        return ;
    }

    device_t * dev = dev_table + dev_id;

    irq_status_t status = irq_enter_protection();
    if(--dev->open_count == 0)
    {
        dev->desc->close(dev); 
        kernel_memset(dev, 0, sizeof(device_t));
    }

    irq_quit_protection(status);    
}